/*
 * @ProjectName: 综合安防
 * @Copyright:   2018 HangZhou Hikvision System Technology Co., Ltd. All Right Reserved.
 * @address:     http://www.hikvision.com
 * @date:        2018年01月29日 14:21
 * @description: 本内容仅限于杭州海康威视系统技术公有限司内部使用，禁止转发.
 */
package com.capsule.chapter.thread;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * <p>
 *
 * </p>
 *
 * @author xiachaoyang
 * @version V1.0
 * @date 2018年09月10日 14:08
 * @modificationHistory=========================逻辑或功能性重大变更记录
 * @modify By: {修改人} 2018年09月10日
 * @modify reason: {方法名}:{原因}
 * ...
 */
public class CountLockService implements Runnable, CountInterface {

    private int loop;

    private Lock lock = new ReentrantLock();

    private LockMethodEnum lockMethodTypeLock;

    public CountLockService(int loop, LockMethodEnum lockMethodTypeLock) {
        this.loop = loop;
        this.lockMethodTypeLock = lockMethodTypeLock;
    }


    @Override
    public void count() {
        Constants.COUNT++;
    }

    /**
     * 中断等待锁的线程
     * @param thread
     * @throws InterruptedException
     */
    @Override
    public void insertThread(Thread thread) throws InterruptedException {
        lock.lockInterruptibly();   //注意，如果需要正确中断等待锁的线程，必须将获取锁放在外面，然后将InterruptedException抛出
        try {
            System.out.println(thread.getName() + "得到了锁");
            long startTime = System.currentTimeMillis();
            for (;;) {
                if (System.currentTimeMillis() - startTime >= 2000){
                    handleCountTask();
                    break;
                }
            }
        } finally {
            System.out.println(Thread.currentThread().getName() + "执行finally");
            lock.unlock();
            System.out.println(thread.getName() + "释放了锁");
        }
        //Thread-2得到了锁
        //Thread-1被中断
        //Thread-5被中断
        //Thread-3被中断
        //Thread-2 >>> get lock
        //Thread-2 >>> count is 1
        //Thread-2执行finally
        //Thread-2释放了锁
        //Thread-4得到了锁
        //Thread-4 >>> get lock
        //Thread-4 >>> count is 2
        //Thread-4执行finally
        //Thread-4释放了锁
    }


    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see Thread#run()
     */
    @Override
    public void run() {
        switch (lockMethodTypeLock) {
            case LOCK_METHOD_TYPE_LOCK:
                doLock();
                break;
            case LOCK_METHOD_TYPE_TRY_LOCK:
                doTryLock();
                break;
        }
    }

    /**
     * 方法是有返回值的，它表示用来尝试获取锁，如果获取成功，则返回true，如果获取失败（即锁已被其他线程获取），则返回false，也就说这个方法无论如何都会立即返回。在拿不到锁时不会一直在那等待
     */
    private void doTryLock() {
        if (lock.tryLock()) {
            try {
                handleCountTask();
            } catch (Exception e) {
                //
            } finally {
                lock.unlock();
                System.out.println(String.format("%s >>> release lock", Thread.currentThread().getName()));
            }
        } else {
            System.out.println(String.format("%s >>> get lock failed!", Thread.currentThread().getName()));
        }
        //...
        //Thread-23 >>> get lock failed!
        //Thread-59 >>> get lock failed!
        //Thread-70 >>> count is 4000
        //Thread-89 >>> get lock failed!
        //Thread-5 >>> get lock failed!
        //Thread-10 >>> get lock failed!
        //...(others failed)
    }

    private void doLock() {
        lock.lock();
        handleCountTask();
        lock.unlock();
        System.out.println(String.format("%s >>> release lock", Thread.currentThread().getName()));
        //Thread-76 >>> get lock
        //Thread-76 >>> release lock
        //Thread-76 >>> count is 10000

    }

    private void handleCountTask() {
        System.out.println(String.format("%s >>> get lock", Thread.currentThread().getName()));
        for (int i = 0; i < loop; i++) {
            count();
        }
        System.out.println(String.format("%s >>> count is %d", Thread.currentThread().getName(), Constants.COUNT));
    }

}
